
#include "tunnelmgr.h"
#include "../server.h"

////////////////////////////////////////////////////////////////////////////////
struct QService
{
    ServiceDescriptor* _desc;

    friend binstream& operator << (binstream& bin, const QService& p )
    {
        charstr vb;
        p._desc->_version.get_version(vb,true);

        charstr flg;
        //abesr-dir flags
        if( p._desc->_flags & ServiceDescriptor::fAUTONOMOUS )                  flg << char('a');  else  flg << char('-');
        if( p._desc->_flags & ServiceDescriptor::fBINDABLE )                    flg << char('b');  else  flg << char('-');
        if( p._desc->_flags & ServiceDescriptor::fSHARED )                      flg << char('e');  else  flg << char('-');
        if( p._desc->_flags & ServiceDescriptor::fHAS_SPAWN_POINT )             flg << char('s');  else  flg << char('-');
        if( p._desc->_flags & ServiceDescriptor::fAUTORUN )                     flg << char('r');  else  flg << char('-');
        flg << char(' ');
        if( p._desc->_flags & ServiceDescriptor::fALLOW_DIRECT_ACCESS )         flg << char('d');  else  flg << char('-');
        if( p._desc->_flags & ServiceDescriptor::fALLOW_INTERPROCESS_ACCESS )   flg << char('i');  else  flg << char('-');
        if( p._desc->_flags & ServiceDescriptor::fALLOW_REMOTE_ACCESS )         flg << char('r');  else  flg << char('-');

        bin << p._desc->_name << p._desc->_info << vb << flg;
        return bin;
    }

    friend binstream& operator >> (binstream& bin, QService& p )
    { throw ersNOT_IMPLEMENTED; }

    friend metastream& operator << (metastream& meta, const QService& p )
    {
        MSTRUCT_OPEN(meta, "coid service");
        MMT(meta, "name", charstr );
        MMT(meta, "info", charstr );
        MMT(meta, "version", charstr );
        MMT(meta, "flags", charstr );
        MSTRUCT_CLOSE(meta);
    }
};

////////////////////////////////////////////////////////////////////////////////
struct QMethod
{
    const ServiceDescriptor::MethodDesc* _meth;

    QMethod( const ServiceDescriptor::MethodDesc& p ) : _meth(&p) { }

    friend binstream& operator >> (binstream& bin, QMethod& p )
    { throw ersNOT_IMPLEMENTED; }

    friend binstream& operator << (binstream& bin, const QMethod& p )
    {
        charstr flg;
        //cm_dir
        if( p._meth->flags & ServiceDescriptor::MethodDesc::fCONST )                    flg << char('c');  else  flg << char('-');
        if( p._meth->flags & ServiceDescriptor::MethodDesc::fMETA )                     flg << char('m');  else  flg << char('-');
        flg << char(' ');
        if( p._meth->flags & ServiceDescriptor::MethodDesc::fACCESS_MODE_DIRECT )       flg << char('d');  else  flg << char('-');
        if( p._meth->flags & ServiceDescriptor::MethodDesc::fACCESS_MODE_INTERPROCESS ) flg << char('i');  else  flg << char('-');
        if( p._meth->flags & ServiceDescriptor::MethodDesc::fACCESS_MODE_REMOTE )       flg << char('r');  else  flg << char('-');

        bin << p._meth->retval << p._meth->name << p._meth->inargs << p._meth->outargs << flg;
        return bin;
    }

    friend metastream& operator << (metastream& meta, const QMethod& p )
    {
        MSTRUCT_OPEN(meta, "coid service method");
        MMT(meta, "ret_val", charstr );
        MMT(meta, "name", charstr );
        MMT(meta, "inargs", dynarray<charstr> );
        MMT(meta, "outargs", dynarray<charstr> );
        MMT(meta, "flags", charstr );
        MSTRUCT_CLOSE(meta);
    }
};

////////////////////////////////////////////////////////////////////////////////
///Get list of available services and their properties
opcd TunnelMgr::qry_services( token svcname, metastream& meta )
{
    opcd e;
    dynarray<uint> lst;
    e = SRVROOTA._objmgr.service_find_all( svcname, lst );
    if(e) return e;

    meta.stream_out( e, "err" );

    dynarray<QService> qs;
    NODEREF<ServiceCoid> svc;
    for( uint i=0; i<lst.size(); ++i )
    {
        e = SRVROOTA._objmgr.get_node( lst[i], svc );
        if(e)  continue;

        QService* ps = qs.add();
        ps->_desc = &svc->_desc;
    }
    
    return meta.stream_out( qs, "services" );
}

////////////////////////////////////////////////////////////////////////////////
opcd TunnelMgr::qry_methods( token svcnamever, metastream& meta )
{
    token svcname = svcnamever.cut_left(':');

    version ver(svcnamever);
    NODEREF<ServiceCoid> svc;

    opcd e = SRVROOTA._objmgr.service_find( svcname, ver, svc );
    if(e) return e;

    meta.stream_out( e, "err" );

    //binstream_typechanging_container<QMethod,ServiceDescriptor::MethodDesc> co( svc->_desc._methflags, svc->_desc._nmeth );
    binstream_container_fixed_array<ServiceDescriptor::MethodDesc,uint>
        co( svc->_desc._methflags, svc->_desc._nmeth + 2 );
    return meta.stream_array_out( co, "methods" );
}

////////////////////////////////////////////////////////////////////////////////
opcd TunnelMgr::qry_types( token svcnamever, const token& type, metastream& meta )
{
    token svcname = svcnamever.cut_left(':');

    version ver(svcnamever);
    NODEREF<ServiceCoid> svc;

    opcd e = SRVROOTA._objmgr.service_find( svcname, ver, svc );
    if(e) return e;

    metastream& svcmeta = svc->_desc._fnc_get_meta();
    svcmeta.bind_formatting_streams(
        meta.get_reading_formatting_stream(),
        meta.get_writing_formatting_stream()
        );

    if( type.is_empty() )
    {
        dynarray<const MetaDesc*> typeinfo;
        svcmeta.get_type_info_all( typeinfo );

        //dynamically define the virtual type descriptor first
        charstr metaname = svcname;
        metaname << "-";
        ver.get_version(metaname);

        uints n = typeinfo.size();

        //actually runs only once
        MSTRUCT_OPEN(svcmeta, metaname)
        for( uints i=0; i<n; ++i )
            MM(svcmeta, typeinfo[i]->type_name, _memberinfo);
        MSTRUCT_CLOSE_NORET(svcmeta)

        //stream the data
        svcmeta.stream_out( e, "err" );

        svcmeta.stream_out_named( metaname, "members" );
        binstream& bin = svcmeta.get_binstream();

        for( uints i=0; i<n; ++i )
            bin << memberinfo(typeinfo[i]);

        bin.flush();
    }
    else
    {
        const MetaDesc* tdesc = svcmeta.get_type_info(type);
        if(!tdesc)
            return ersUNKNOWN;

        svcmeta.stream_out( e, "err" );
        svcmeta.stream_out( memberinfo(tdesc), tdesc->type_name );
    }

    return 0;
}
